----------------------------------------------------------------------------------
-- CPEG 422 spartan-3e base
-- Jason Parrott
-- Rodney McGee
-- Ray Delvecchio
-- Juergen Ributzka
-- Jack Welsh
-- Ryan Hoover
-- Mike Stamat
-- Lee Reed
--
-- LCD Wrapper
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;


entity LCD_top is
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
			  keyboard_data : in STD_LOGIC_VECTOR (7 downto 0);
			  keyboard_ready : in STD_LOGIC;
           LCD_D : out  STD_LOGIC_VECTOR (11 downto 8);
           LCD_E : out  STD_LOGIC;
           LCD_RS : out  STD_LOGIC;
           LCD_RW : out  STD_LOGIC);
end LCD_top;

architecture Behavioral of LCD_top is

component LCD
    Port ( clk : in  STD_LOGIC;
           reset : in  STD_LOGIC;
           data_in : in  STD_LOGIC_VECTOR (7 downto 0);
           enable : in STD_LOGIC;
           LCD_D : out  STD_LOGIC_VECTOR (11 downto 8);
           LCD_E : out  STD_LOGIC;
           finished : out  STD_LOGIC);
end component;

signal data_in : STD_LOGIC_VECTOR (7 downto 0) := (others => '0');
signal enable : STD_LOGIC := '0';
signal finished :   STD_LOGIC;
signal finished_prev: STD_LOGIC;

type statetype is (S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13);
signal state, next_state : statetype := S0;

begin

LCD_RW <= '0';
state <= next_state;
 
UUT: LCD port map(clk, reset, data_in, enable, LCD_D, LCD_E, finished);

process (clk, reset) is
variable addrCount: std_logic_vector (3 downto 0) := "0000";
variable secondLine: std_logic := '0';
begin
		 if (reset = '1') then
			enable <= '0';
			addrCount := "0000";
			secondLine := '0';
			next_state <= S0;
			finished_prev <= '0';
			LCD_RS <= '0';
		 elsif (rising_edge(clk)) then

				LCD_RS <= '0';
			
					case state is

					--States S0-S5: Display Configuration
					--S0: If rising edge of finished is detected, write data
					--0x28 = Function set - Spartan3E only supports this command
					when S0 =>
							  if (finished = '1' and finished_prev = '0') then
										 data_in <= x"28";
										 enable <= '1';
										 next_state <= S1;
							  else
										 next_state <= S0;
							  end if;

					--S1: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing
					when S1 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S2;
							  else
										 next_state <= S1;
							  end if;
							  
					--S2: 0x01 = Clear Display
					--Clear display and return cursor to home position
					when S2 =>
						LCD_RS <= '0';
						secondLine := '0';
						data_in <= x"01";
						enable <= '1';
						next_state <= S3;
					
					--S3: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing								
					when S3 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S4;
							  else
										 next_state <= S3;
							  end if;

					
					--S4: 0x06 = Entry Mode Set
					--		Set to auto-increment DDRAM address counter
					--		Disable shifting of characters
					when S4 =>
							  data_in <= x"06";
							  enable <= '1';
							  next_state <= S5;
							  
					--S5: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing  
					when S5 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S6;
							  else
										 next_state <= S5;
							  end if;
							  
					--S6: 0x0F = Display On/Off
					--		Display/Cursor/Blinking all on
					when S6 =>
							  data_in <= x"0F";
							  enable <= '1';
							  next_state <= S7;
							  
					--S7: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing  
					when S7 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S8;
							  else
										 next_state <= S7;
							  end if;

					--S8: Set DDRAM address for 1st or 2nd line
					when S8 =>
						if (addrCount = "1111" and secondLine = '1') then
							data_in <= x"C0";
							addrCount := "0000";
						else
							data_in <= x"80";
							addrCount := "0000";
						end if;
						
						enable <= '1';
						next_state <= S9;
					
					--S9: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing 								
					when S9 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S10;
							  else
										 next_state <= S9;
							  end if;
												  
					--S10: Process keyboard input
					when S10 =>
						if(keyboard_ready = '1') then
							if (keyboard_data = x"00") then -- Unknown character
								next_state <= S10;
						   elsif (keyboard_data  = x"08") then -- Backspace
								next_state <= S12;
						   elsif (keyboard_data  = x"0d") then -- Return
								next_state <= S2;
							else
								LCD_RS <= '1';
								data_in <= keyboard_data;
								enable <= '1';
								next_state <= S11;
							end if;
						else
							LCD_RS <= '0';
							enable <= '0';
							next_state <= S10;
						end if;
							  
					when S11 =>
							LCD_RS <= '1';
							  if (finished = '1' and finished_prev = '0') then
										enable <= '0';
										if(addrCount = "1111") then
											secondLine := not secondLine;
											next_state <= S8;
										else
											addrCount := addrCount + 1;
											next_state <= S10;
										end if;
							  else
										next_state <= S11;
							  end if;

					--S12: Move cursor one left
					when S12 =>
						LCD_RS <= '0';
						data_in <= x"10";
						addrCount := addrCount - 1;
						
						enable <= '1';
						next_state <= S13;
					
					--S13: If rising edge of finish is detected, write is complete
					--		Set enable low for one cycle before continuing 								
					when S13 =>
							  if (finished = '1' and finished_prev = '0') then
										 enable <= '0';
										 next_state <= S10;
							  else
										 next_state <= S13;
							  end if;
					end case;

					finished_prev <= finished;
		 end if;
end process;

end Behavioral;